﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Windows.Media.Imaging;

namespace 修为尽失.辅助.exts
{
    public static class String
    {
        public static Bitmap Base64StringToBitmap(this string basestr)
        {
            Bitmap bitmap = null;
            try
            {
                var arr = Convert.FromBase64String(basestr);
                var ms = new MemoryStream(arr);
                bitmap = new Bitmap(ms);
                ms.Close();
            }
            catch (Exception ex)
            {
               // MessageManager.Send(new Message(ex.ToLogString(), MessageType.错误));
               ex.ShowException();
                return null;
            }

            return bitmap;
        }

        public static BitmapSource Base64StringToBitmapSource(this string basestr)
        {
            try
            {
                var bi = new BitmapImage();
                bi.BeginInit();
                var arr = Convert.FromBase64String(basestr);
                var ms = new MemoryStream(arr);
                bi.StreamSource = ms;
                bi.CacheOption = BitmapCacheOption.OnLoad;
                bi.EndInit();
                ms.Close();
                return bi;
            }
            catch (Exception ex)
            {
                ex.ShowException();
                return null;
            }
        }

        public static string X(this string s, int n)
        {
            var str = string.Empty;
            for (int i = 0; i < n; i++)
            {
                str += s;
            }

            return str;
        }

        public static int WordSimpleCount(this string str) => str.Split(new char[] {' ', '.', '?'}, StringSplitOptions.RemoveEmptyEntries).Length;

        public static string GetMd5Hash(this string input, MD5 md5Hash)
        {
            // Convert the input string to a byte array and compute the hash.
            var data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));

            // Create a new Stringbuilder to collect the bytes
            // and create a string.
            var sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            for (var i = 0; i < data.Length; i++)
                sBuilder.Append(data[i].ToString("x2"));

            // Return the hexadecimal string.
            return sBuilder.ToString();
        }

        // Verify a hash against a string.
        public static bool VerifyMd5Hash(this string input, MD5 md5Hash, string hash)
        {
            // Hash the input.
            var hashOfInput = GetMd5Hash(input, md5Hash);

            // Create a StringComparer an compare the hashes.
            var comparer = StringComparer.OrdinalIgnoreCase;
            if (0 == comparer.Compare(hashOfInput, hash))
                return true;
            else
                return false;
        }

        public static int GetWidth(this string a)
        {
            var result = 0;
            foreach (int c in a)
            {
                result = c >= 0 && c < 127 ? result + 1 : result + 2;
            }

            return result;
        }

        public static bool Contains(this string value, string match, StringComparison comparison = StringComparison.CurrentCultureIgnoreCase) => value.IndexOf(match, comparison) > -1;

        public static bool ContainsAll(this string s, IEnumerable<string> keys)
        {
            bool r = false;
            foreach (var item in keys)
            {
                r = s.Contains(item);
                if (!r)
                    return false;
            }

            return r;
        }

        public static bool MatchAny(this string s, IEnumerable<string> keys)
        {
           
            foreach (var item in keys)
            {
                if (s == item)
                    return true;;
            }

            return false;
        }
        public static bool ContainsAny(this string s, IList<string> keys)
        {
            bool r = false;
            foreach (var item in keys)
            {
                r = s.Contains(item);
                break;
            }

            return r;
        }

        public static bool In(this string value, params string[] stringValues)
        {
            foreach (string otherValue in stringValues)
                if (System.String.CompareOrdinal(value, otherValue) == 0)
                    return true;
            return false;
        }

        public static string Right(this string value, int length)
        {
            return value != null && value.Length > length ? value.Substring(value.Length - length) : value;
        }

        public static string Left(this string value, int length)
        {
            return value != null && value.Length > length ? value.Substring(0, length) : value;
        }

        public static void AppendKeyValue(this StringBuilder builder, string key, object value, string end = " ")
        {
            if ((value == null) || IsInvalid(value.ToString()))
                return;
            builder.Append($"{(object) key}:{value}{(object) end}");
        }

        public static void AppendKeyValue(this StringBuilder sb, string key, Enum value, string end = " ") => sb.Append(key).Append(":").Append(value.GetDescription()).Append(end);

        public static void AppendKeyValueNewLine(this StringBuilder sb, string key, object value)
        {
            if ((value == null) || IsInvalid(value.ToString()))
                return;
            sb.Append(key).Append(":").Append(value).Append(Environment.NewLine);
        }

        public static void AppendKeyValueNewLine(this StringBuilder sb, string key, Enum value) => sb.Append(key).Append(":").Append(value.GetDescription()).Append(Environment.NewLine);

        public static string Encode(this string value, Encoding encode)
        {
            Guard.ArgumentNotNull((object) value, nameof(value));
            var unicode = Encoding.Unicode;
            return Encoding.Convert(unicode, encode, value.ToBytes(unicode)).GetString(encode);
        }

        public static string Encode(this string value, Encoding sencode, Encoding tencode)
        {
            Guard.ArgumentNotNull((object) value, nameof(value));
            return Encoding.Convert(sencode, tencode, value.ToBytes(sencode)).GetString(tencode);
        }

        public static string Format(this string @this, object arg0) => string.Format(@this, arg0);
        public static string Format(this string @this, params object[] arg0) => string.Format(@this, arg0);
        public static string Format(this string @this, object arg0, object arg1) => string.Format(@this, arg0, arg1);
        public static string Format(this string @this, object arg0, object arg1, object arg2) => string.Format(@this, arg0, arg1, arg2);

        public static string HtmlDecode(this string source)
        {
            if (!string.IsNullOrEmpty(source))
                return HttpUtility.HtmlDecode(source);
            return string.Empty;
        }

        public static string HtmlEncode(this string source)
        {
            if (!string.IsNullOrEmpty(source))
                return HttpUtility.HtmlEncode(source);
            return string.Empty;
        }

        public static byte[] ToASCIIBytes(this string value) => value.ToBytes(Encoding.ASCII);
        public static bool IsChinese(this char value) => ((int) value >= 19968) && ((int) value <= 40891);
        public static bool IsHasChinese(this string value) => value.Any<char>((Func<char, bool>) (c => c.IsChinese()));
        public static bool IsInvalid(this string value) => value.IsNullOrEmptyOrWhiteSpace();

        public static bool IsMatch(this string source, string pattern)
        {
            if (IsInvalid(source))
                return false;
            return source.IsMatch(pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);
        }

        public static bool IsMatch(this string source, string pattern, RegexOptions options)
        {
            if (IsInvalid(source))
                return false;
            return Regex.IsMatch(source, pattern, options);
        }

        public static bool IsNullOrEmpty(this string value) => string.IsNullOrEmpty(value);

        public static bool IsNullOrEmptyOrWhiteSpace(this string value)
        {
            var flag = value.IsNullOrEmpty();
            if (!flag)
                flag = value.Trim().IsNullOrEmpty();
            return flag;
        }

        public static bool ToBoolean(this string value)
        {
            bool result;
            if (bool.TryParse(value, out result))
                return result;
            throw new InvalidCastException("\"" + value + "\"不是有效的boolean，请确认。");
        }

        public static byte[] ToBytes(this string value) => value.ToBytes(Encoding.Default);
        public static byte[] ToBytes(this string value, Encoding encode) => encode.GetBytes(value);

        public static int ToInt(this string value)
        {
            int result;
            if (int.TryParse(value, out result))
                return result;
            throw new InvalidCastException("不能将字符串\"" + value + "\"转换为整型数字。");
        }

        public static short ToInt16(this string value)
        {
            short result;
            if (short.TryParse(value, out result))
                return result;
            throw new InvalidCastException("不能将字符串\"" + value + "\"转换为短整型数字。");
        }

        public static long ToInt64(this string value)
        {
            long result;
            if (long.TryParse(value, out result))
                return result;
            throw new InvalidCastException("不能将字符串\"" + value + "\"转换为长整型数字。");
        }

        public static bool ToSafeBoolean(this string value, bool defaultValue = false)
        {
            bool result;
            if (bool.TryParse(value, out result))
                return result;
            return defaultValue;
        }

        public static byte ToSafeByte(this string value)
        {
            byte result;
            if (byte.TryParse(value, out result))
                return result;
            return 0;
        }

        public static DateTime? ToSafeDateTime(this string dateTime)
        {
            DateTime result;
            if (DateTime.TryParse(dateTime, out result))
                return new DateTime?(result);
            return new DateTime?();
        }

        public static Decimal ToSafeDecimal(this string value)
        {
            Decimal result;
            if (Decimal.TryParse(value, out result))
                return result;
            return new Decimal(0);
        }

        public static double ToSafeDouble(this string value)
        {
            double result;
            if (double.TryParse(value, out result))
                return result;
            return 0.0;
        }

        public static T ToSafeEnum<T>(this string value, T defaultValue)
        {
            try
            {
                return value.ToEnum<T>();
            }
            catch
            {
                return defaultValue;
            }
        }

        public static int ToSafeInt(this string value)
        {
            if (value.Contains(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator))
                return (int) value.ToSafeDouble();
            int result;
            if (int.TryParse(value, out result))
                return result;
            return 0;
        }

        public static short ToSafeInt16(this string value)
        {
            short result;
            if (short.TryParse(value, out result))
                return result;
            return 0;
        }

        public static long ToSafeInt64(this string value)
        {
            if (value.Contains(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator))
                return (long) value.ToSafeDouble();
            long result;
            if (long.TryParse(value, out result))
                return result;
            return 0;
        }

        public static string ToSafeString(this object obj)
        {
            if (obj == null)
                return string.Empty;
            return obj.ToString();
        }

        public static ulong ToSafeUlong(this string value)
        {
            if (string.IsNullOrWhiteSpace(value))
                return 0;
            if (value.Contains(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator))
                return (ulong) value.ToSafeDouble();
            ulong result;
            if (ulong.TryParse(value, out result))
                return result;
            return 0;
        }

        public static Version ToSafeVersion(this string value)
        {
            var result = new Version();
            if (IsInvalid(value) || Version.TryParse(value, out result))
                return result;
            var matchCollection = Regex.Matches(value, "(\\d{1,8}\\.){1,3}\\d{1,8}", RegexOptions.IgnoreCase);
            if (matchCollection.Count == 0)
                return result;
            return matchCollection[0].ToSafeString().ToSafeVersion();
        }

        public static string ToShortString(this Version value)
        {
            if (!value.IsValid())
                return string.Empty;
            if ((value.Minor <= 0) && (value.Build <= 0) && (value.Revision <= 0))
                return $"{(object) value.Major}";
            if ((value.Build <= 0) && (value.Revision <= 0))
                return $"{(object) value.Major}.{(object) value.Minor}";
            if (value.Revision <= 0)
                return $"{(object) value.Major}.{(object) value.Minor}.{(object) value.Build}";
            return $"{(object) value.Major}.{(object) value.Minor}.{(object) value.Build}.{(object) value.Revision}";
        }

        public static DateTime? ToUTCTime(this string dateTime)
        {
            var safeDateTime = dateTime.ToSafeDateTime();
            if (safeDateTime.HasValue)
                return safeDateTime;
            return new Regex("(?i)\\d+ [a-z]+ \\d{4} \\d+:\\d+:\\d+ [-\\+]\\d+").Match(dateTime).Value.ToSafeDateTime();
        }

        public static string TrimEnd(this string value, string end, StringComparison comparison = StringComparison.Ordinal)
        {
            if (IsInvalid(value))
                return value;
            if (IsInvalid(end))
                return value.TrimEnd();
            if (!value.EndsWith(end, comparison))
                return value;
            var length = value.LastIndexOf(end, comparison);
            return value.Substring(0, length);
        }

        public static string TrimStart(this string value, string star)
        {
            if (IsInvalid(star))
                return value.TrimStart();
            if (!value.StartsWith(star))
                return value;
            return value.Substring(star.Length);
        }
    }
}